{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<center>\n",
    "<img src=\"https://habrastorage.org/web/677/8e1/337/6778e1337c3d4b159d7e99df94227cb2.jpg\"/>\n",
    "## Специализация \"Машинное обучение и анализ данных\"\n",
    "<center>Автор материала: программист-исследователь Mail.Ru Group, старший преподаватель Факультета Компьютерных Наук ВШЭ [Юрий Кашницкий](https://yorko.github.io/)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# <center> Capstone проект №1 <br> Идентификация пользователей по посещенным веб-страницам\n",
    "<img src='http://i.istockimg.com/file_thumbview_approve/21546327/5/stock-illustration-21546327-identification-de-l-utilisateur.jpg'>\n",
    "\n",
    "# <center>Неделя 4.  Сравнение алгоритмов классификации\n",
    "\n",
    "Теперь мы наконец подойдем к обучению моделей классификации, сравним на кросс-валидации несколько алгоритмов, разберемся, какие параметры длины сессии (*session_length* и *window_size*) лучше использовать. Также для выбранного алгоритма построим кривые валидации (как качество классификации зависит от одного из гиперпараметров алгоритма) и кривые обучения (как качество классификации зависит от объема выборки).\n",
    "\n",
    "**План 4 недели:**\n",
    "- Часть 1. Сравнение нескольких алгоритмов на сессиях из 10 сайтов\n",
    "- Часть 2. Выбор параметров – длины сессии и ширины окна\n",
    "- Часть 3. Идентификация  конкретного пользователя и кривые обучения\n",
    " \n",
    "\n",
    "\n",
    "**В этой части проекта Вам могут быть полезны видеозаписи следующих лекций курса \"Обучение на размеченных данных\":**\n",
    "   - [Линейная классификация](https://www.coursera.org/learn/supervised-learning/lecture/jqLcO/linieinaia-klassifikatsiia)\n",
    "   - [Сравнение алгоритмов и выбор гиперпараметров](https://www.coursera.org/learn/supervised-learning/lecture/aF79U/sravnieniie-alghoritmov-i-vybor-ghipierparamietrov)\n",
    "   - [Кросс-валидация. Sklearn.cross_validation](https://www.coursera.org/learn/supervised-learning/lecture/XbHEk/kross-validatsiia-sklearn-cross-validation)\n",
    "   - [Линейные модели. Sklearn.linear_model. Классификация](https://www.coursera.org/learn/supervised-learning/lecture/EBg9t/linieinyie-modieli-sklearn-linear-model-klassifikatsiia)\n",
    "   - и многие другие\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Задание\n",
    "1. Заполните код в этой тетрадке \n",
    "2. Если вы проходите специализацию Яндеса и МФТИ, пошлите файл с ответами в соответствующем Programming Assignment. <br> Если вы проходите курс ODS, выберите ответы в [веб-форме](https://docs.google.com/forms/d/12VB7kmzDoSVzSpQNaJp0tR-2t8K8PynQopP3dypf7i4).  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# pip install watermark\n",
    "%load_ext watermark"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%watermark -v -m -p numpy,scipy,pandas,matplotlib,statsmodels,sklearn -g"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from __future__ import division, print_function\n",
    "# отключим всякие предупреждения Anaconda\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "from time import time\n",
    "import itertools\n",
    "import os\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import seaborn as sns\n",
    "%matplotlib inline\n",
    "from matplotlib import pyplot as plt\n",
    "import pickle\n",
    "from scipy.sparse import csr_matrix\n",
    "from sklearn.model_selection import train_test_split, cross_val_score, StratifiedKFold, GridSearchCV\n",
    "from sklearn.metrics import accuracy_score, f1_score"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Поменяйте на свой путь к данным\n",
    "PATH_TO_DATA = 'capstone_user_identification'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Часть 1. Сравнение нескольких алгоритмов на сессиях из 10 сайтов"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Загрузим сериализованные ранее объекты *X_sparse_10users* и *y_10users*, соответствующие обучающей выборке для 10 пользователей.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open(os.path.join(PATH_TO_DATA, \n",
    "         'X_sparse_10users.pkl'), 'rb') as X_sparse_10users_pkl:\n",
    "    X_sparse_10users = pickle.load(X_sparse_10users_pkl)\n",
    "with open(os.path.join(PATH_TO_DATA, \n",
    "                       'y_10users.pkl'), 'rb') as y_10users_pkl:\n",
    "    y_10users = pickle.load(y_10users_pkl)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Здесь более 14 тысяч сессий и почти 5 тысяч уникальных посещенных сайтов.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_sparse_10users.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Разобьем выборку на 2 части. На одной будем проводить кросс-валидацию, на второй – оценивать модель, обученную после кросс-валидации.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train, X_valid, y_train, y_valid = train_test_split(X_sparse_10users, y_10users, \n",
    "                                                      test_size=0.3, \n",
    "                                                     random_state=17, stratify=y_10users)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Зададим заранее тип кросс-валидации: 3-кратная, с перемешиванием, параметр random_state=17 – для воспроизводимости.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "skf = StratifiedKFold(n_splits=3, shuffle=True, random_state=17)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Вспомогательная функция для отрисовки кривых валидации после запуска GridSearchCV (или RandomizedCV).**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_validation_curves(param_values, grid_cv_results_):\n",
    "    train_mu, train_std = grid_cv_results_['mean_train_score'], grid_cv_results_['std_train_score']\n",
    "    valid_mu, valid_std = grid_cv_results_['mean_test_score'], grid_cv_results_['std_test_score']\n",
    "    train_line = plt.plot(param_values, train_mu, '-', label='train', color='green')\n",
    "    valid_line = plt.plot(param_values, valid_mu, '-', label='test', color='red')\n",
    "    plt.fill_between(param_values, train_mu - train_std, train_mu + train_std, edgecolor='none',\n",
    "                     facecolor=train_line[0].get_color(), alpha=0.2)\n",
    "    plt.fill_between(param_values, valid_mu - valid_std, valid_mu + valid_std, edgecolor='none',\n",
    "                     facecolor=valid_line[0].get_color(), alpha=0.2)\n",
    "    plt.legend()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**1. Обучите `KNeighborsClassifier` со 100 ближайшими соседями (остальные параметры оставьте по умолчанию, только `n_jobs`=-1 для распараллеливания) и посмотрите на долю правильных ответов на 3-кратной кросс-валидации (ради воспроизводимости используйте для этого объект `StratifiedKFold` `skf`) по выборке `(X_train, y_train)` и отдельно на выборке `(X_valid, y_valid)`.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.neighbors import KNeighborsClassifier"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "knn = KNeighborsClassifier ''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**<font color='red'>Вопрос 1. </font> Посчитайте доли правильных ответов для KNeighborsClassifier на кросс-валидации и отложенной выборке. Округлите каждое до 3 знаков после запятой и введите через пробел.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**2. Обучите случайный лес (`RandomForestClassifier`) из 100 деревьев (для воспроизводимости `random_state`=17). Посмотрите на OOB-оценку (для этого надо сразу установить `oob_score`=True) и на долю правильных ответов на выборке `(X_valid, y_valid)`. Для распараллеливания задайте `n_jobs`=-1.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.ensemble import RandomForestClassifier"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "forest = RandomForestClassifier ''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**<font color='red'>Вопрос 2. </font> Посчитайте доли правильных ответов для `RandomForestClassifier` при Out-of-Bag оценке и на отложенной выборке. Округлите каждое до 3 знаков после запятой и введите через пробел.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "write_answer_to_file(''' ВАШ КОД ЗДЕСЬ ''',\n",
    "                     'answer4_2.txt')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!cat answer4_2.txt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**3. Обучите логистическую регрессию (`LogisticRegression`) с параметром `C` по умолчанию и `random_state`=17 (для воспроизводимости). Посмотрите на долю правильных ответов на кросс-валидации (используйте объект `skf`, созданный ранее) и на выборке `(X_valid, y_valid)`. Для распараллеливания задайте `n_jobs=-1`.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.linear_model import LogisticRegression, LogisticRegressionCV"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "logit = LogisticRegression ''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Почитайте документацию к [LogisticRegressionCV](http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegressionCV.html). Логистическая регрессия хорошо изучена, и для нее существуют алгоритмы быстрого подбора параметра регуляризации `C` (быстрее, чем с `GridSearchCV`).**\n",
    "\n",
    "**С помощью `LogisticRegressionCV` подберите параметр `C` для `LogisticRegression` сначала в широком диапазоне: 10 значений от 1e-4 до 1e2, используйте `logspace` из `NumPy`. Укажите у `LogisticRegressionCV` параметры `multi_class`='multinomial' и `random_state`=17. Для кросс-валидации используйте объект `skf`, созданный ранее. Для распараллеливания задайте `n_jobs=-1`.**\n",
    "\n",
    "**Нарисуйте кривые валидации по параметру `C`.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "logit_c_values1 = np.logspace(-4, 2, 10)\n",
    "\n",
    "logit_grid_searcher1 = LogisticRegressionCV ''' ВАШ КОД ЗДЕСЬ ''' \n",
    "logit_grid_searcher1.fit(X_train, y_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Средние значения доли правильных ответов на кросс-валидации по каждому из 10 параметров `C`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "logit_mean_cv_scores1 = ''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Выведите лучшее значение доли правильных ответов на кросс-валидации и соответствующее значение `C`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Нарисуйте график зависимости доли правильных ответов на кросс-валидации от `C`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.plot(logit_c_values1, logit_mean_cv_scores1);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Теперь то же самое, только значения параметра `C` перебирайте в диапазоне `np.linspace`(0.1, 7, 20). Опять нарисуйте кривые валидации, определите максимальное значение доли правильных ответов на кросс-валидации.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "logit_c_values2 = np.linspace(0.1, 7, 20)\n",
    "\n",
    "logit_grid_searcher2 = LogisticRegressionCV ''' ВАШ КОД ЗДЕСЬ '''\n",
    "logit_grid_searcher2.fit(X_train, y_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Средние значения доли правильных ответов на кросс-валидации по каждому из 10 параметров `C`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Выведите лучшее значение доли правильных ответов на кросс-валидации и соответствующее значение `C`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Нарисуйте график зависимости доли правильных ответов на кросс-валидации от `C`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.plot(logit_c_values2, logit_mean_cv_scores2);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Выведите долю правильных ответов на выборке `(X_valid, y_valid)` для логистической регрессии с лучшим найденным значением `C`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "logit_cv_acc = accuracy_score ''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**<font color='red'>Вопрос 3. </font>Посчитайте доли правильных ответов для `logit_grid_searcher2` на кросс-валидации для лучшего значения параметра `C` и на отложенной выборке. Округлите каждое до 3 знаков после запятой и выведите через пробел.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**4. Обучите линейный SVM (`LinearSVC`) с параметром `C`=1 и `random_state`=17 (для воспроизводимости). Посмотрите на долю правильных ответов на кросс-валидации (используйте объект `skf`, созданный ранее) и на выборке `(X_valid, y_valid)`.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.svm import LinearSVC"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "svm = LinearSVC ''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**С помощью `GridSearchCV` подберите параметр `C` для SVM сначала в широком диапазоне: 10 значений от 1e-4 до 1e4, используйте `linspace` из NumPy. Нарисуйте кривые валидации.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "svm_params1 = {'C': np.linspace(1e-4, 1e4, 10)}\n",
    "\n",
    "svm_grid_searcher1 = GridSearchCV ''' ВАШ КОД ЗДЕСЬ '''\n",
    "svm_grid_searcher1.fit(X_train, y_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Выведите лучшее значение доли правильных ответов на кросс-валидации и соответствующее значение `C`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Нарисуйте график зависимости доли правильных ответов на кросс-валидации от `C`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_validation_curves(svm_params1['C'], svm_grid_searcher1.cv_results_)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Но мы помним, что с параметром регуляризации по умолчанию (С=1) на кросс-валидации доля правильных ответов выше. Это тот случай (не редкий), когда можно ошибиться и перебирать параметры не в том диапазоне (причина в том, что мы взяли равномерную сетку на большом интервале и упустили действительно хороший интервал значений `C`). Здесь намного осмысленней подбирать `C` в районе 1, к тому же, так модель быстрее обучается, чем при больших `C`. **\n",
    "\n",
    "**С помощью `GridSearchCV` подберите параметр `C` для SVM в диапазоне (1e-3, 1), 30 значений, используйте `linspace` из NumPy. Нарисуйте кривые валидации.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "svm_params2 = {'C': np.linspace(1e-3, 1, 30)}\n",
    "\n",
    "svm_grid_searcher2 = GridSearchCV ''' ВАШ КОД ЗДЕСЬ '''\n",
    "svm_grid_searcher2.fit(X_train, y_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Выведите лучшее значение доли правильных ответов на кросс-валидации и соответствующее значение `C`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Нарисуйте график зависимости доли правильных ответов на кросс-валидации от С."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_validation_curves(svm_params2['C'], svm_grid_searcher2.cv_results_)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Выведите долю правильных ответов на выборке `(X_valid, y_valid)` для `LinearSVC` с лучшим найденным значением `C`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "svm_cv_acc = accuracy_score ''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**<font color='red'>Вопрос 4. </font> Посчитайте доли правильных ответов для `svm_grid_searcher2` на кросс-валидации для лучшего значения параметра `C` и на отложенной выборке. Округлите каждое до 3 знаков после запятой и выведите через пробел.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Часть 2. Выбор параметров – длины сессии и ширины окна"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Возьмем `LinearSVC`, показавший лучшее качество на кросс-валидации в 1 части, и проверим его работу еще на 8 выборках для 10 пользователей (с разными сочетаниями параметров *session_length* и *window_size*). Поскольку тут уже вычислений побольше, мы не будем каждый раз заново подбирать параметр регуляризации `C`.**\n",
    "\n",
    "**Определите функцию `model_assessment`, ее документация описана ниже. Обратите внимание на все детали. Например, на то, что разбиение  выборки с `train_test_split` должно быть стратифицированным. Не теряйте нигде `random_state`.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def model_assessment(estimator, path_to_X_pickle, path_to_y_pickle, cv, random_state=17, test_size=0.3):\n",
    "    '''\n",
    "    Estimates CV-accuracy for (1 - test_size) share of (X_sparse, y) \n",
    "    loaded from path_to_X_pickle and path_to_y_pickle and holdout accuracy for (test_size) share of (X_sparse, y).\n",
    "    The split is made with stratified train_test_split with params random_state and test_size.\n",
    "    \n",
    "    :param estimator – Scikit-learn estimator (classifier or regressor)\n",
    "    :param path_to_X_pickle – path to pickled sparse X (instances and their features)\n",
    "    :param path_to_y_pickle – path to pickled y (responses)\n",
    "    :param cv – cross-validation as in cross_val_score (use StratifiedKFold here)\n",
    "    :param random_state –  for train_test_split\n",
    "    :param test_size –  for train_test_split\n",
    "    \n",
    "    :returns mean CV-accuracy for (X_train, y_train) and accuracy for (X_valid, y_valid) where (X_train, y_train)\n",
    "    and (X_valid, y_valid) are (1 - test_size) and (testsize) shares of (X_sparse, y).\n",
    "    '''\n",
    "    \n",
    "    ''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Убедитесь, что функция работает.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model_assessment(svm_grid_searcher2.best_estimator_, \n",
    "                 os.path.join(PATH_TO_DATA, 'X_sparse_10users.pkl'),\n",
    "        os.path.join(PATH_TO_DATA, 'y_10users.pkl'), skf, random_state=17, test_size=0.3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Примените функцию *model_assessment* для лучшего алгоритма из предыдущей части (а именно, `svm_grid_searcher2.best_estimator_`) и 9 выборок вида с разными сочетаниями параметров *session_length* и *window_size* для 10 пользователей. Выведите в цикле параметры *session_length* и *window_size*, а также результат вывода функции *model_assessment*. \n",
    "Удобно сделать так, чтоб *model_assessment* возвращала 3-им элементом время, за которое она выполнилась. На моем ноуте этот участок кода выполнился за 20 секунд. Но со 150 пользователями каждая итерация занимает уже несколько минут.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Здесь для удобства стоит создать копии ранее созданных pickle-файлов X_sparse_10users.pkl, X_sparse_150users.pkl, y_10users.pkl и y_150users.pkl, добавив к их названиям s10_w10, что означает длину сессии 10 и ширину окна 10. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!cp $PATH_TO_DATA/X_sparse_10users.pkl $PATH_TO_DATA/X_sparse_10users_s10_w10.pkl \n",
    "!cp $PATH_TO_DATA/X_sparse_150users.pkl $PATH_TO_DATA/X_sparse_150users_s10_w10.pkl \n",
    "!cp $PATH_TO_DATA/y_10users.pkl $PATH_TO_DATA/y_10users_s10_w10.pkl \n",
    "!cp $PATH_TO_DATA/y_150users.pkl $PATH_TO_DATA/y_150users_s10_w10.pkl "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "estimator = svm_grid_searcher2.best_estimator_\n",
    "\n",
    "for window_size, session_length in itertools.product([10, 7, 5], [15, 10, 7, 5]):\n",
    "    if window_size <= session_length:\n",
    "        path_to_X_pkl = ''' ВАШ КОД ЗДЕСЬ '''\n",
    "        path_to_y_pkl = ''' ВАШ КОД ЗДЕСЬ '''\n",
    "        print           ''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**<font color='red'>Вопрос 5. </font> Посчитайте доли правильных ответов для `LinearSVC` с настроенным параметром `C` и выборки `X_sparse_10users_s15_w5`. Укажите доли правильных ответов на кросс-валидации и на отложенной выборке. Округлите каждое до 3 знаков после запятой и выведите через пробел.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Прокомментируйте полученные результаты. Сравните для 150 пользователей доли правильных ответов на кросс-валидации и оставленной выборке для сочетаний параметров (*session_length, window_size*): (5,5), (7,7) и (10,10). На среднем ноуте это может занять до часа – запаситесь терпением, это Data Science :) **\n",
    "\n",
    "**Сделайте вывод о том, как качество классификации зависит от длины сессии и ширины окна.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "estimator = svm_grid_searcher2.best_estimator_\n",
    "\n",
    "for window_size, session_length in [(5,5), (7,7), (10,10)]:\n",
    "    path_to_X_pkl = ''' ВАШ КОД ЗДЕСЬ '''\n",
    "    path_to_y_pkl = ''' ВАШ КОД ЗДЕСЬ '''\n",
    "    print           ''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**<font color='red'>Вопрос 6. </font> Посчитайте доли правильных ответов для `LinearSVC` с настроенным параметром `C` и выборки `X_sparse_150users`. Укажите доли правильных ответов на кросс-валидации и на отложенной выборке. Округлите каждое до 3 знаков после запятой и выведите через пробел.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Часть 3. Идентификация  конкретного пользователя и кривые обучения"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Поскольку может разочаровать, что многоклассовая доля правильных ответов на выборке из 150 пользовалей невелика, порадуемся тому, что конкретного пользователя можно идентифицировать достаточно хорошо. **"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Загрузим сериализованные ранее объекты *X_sparse_150users* и *y_150users*, соответствующие обучающей выборке для 150 пользователей с параметрами (*session_length, window_size*) = (10,10). Так же точно разобьем их на 70% и 30%.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open(os.path.join(PATH_TO_DATA, 'X_sparse_150users.pkl'), 'rb') as X_sparse_150users_pkl:\n",
    "     X_sparse_150users = pickle.load(X_sparse_150users_pkl)\n",
    "with open(os.path.join(PATH_TO_DATA, 'y_150users.pkl'), 'rb') as y_150users_pkl:\n",
    "    y_150users = pickle.load(y_150users_pkl)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train_150, X_valid_150, y_train_150, y_valid_150 = train_test_split(X_sparse_150users, \n",
    "                                                                      y_150users, test_size=0.3, \n",
    "                                                     random_state=17, stratify=y_150users)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Обучите `LogisticRegressionCV` для одного значения параметра `C` (лучшего на кросс-валидации в 1 части, используйте точное значение, не на глаз). Теперь будем решать 150 задач \"Один-против-Всех\", поэтому укажите аргумент `multi_class`='ovr'. Как всегда, где возможно, указывайте `n_jobs=-1` и `random_state`=17.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "logit_cv_150users = LogisticRegressionCV ''' ВАШ КОД ЗДЕСЬ '''\n",
    "logit_cv_150users.fit(X_train_150, y_train_150)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Посмотрите на средние доли правильных ответов на кросс-валидации в задаче идентификации каждого пользователя по отдельности.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cv_scores_by_user = {}\n",
    "for user_id in logit_cv_150users.scores_:\n",
    "    print('User {}, CV score: {}'.format ''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Результаты кажутся впечатляющими, но возможно, мы забываем про дисбаланс классов, и высокую долю правильных ответов можно получить константным прогнозом. Посчитайте для каждого пользователя разницу между долей правильных ответов на кросс-валидации (только что посчитанную с помощью `LogisticRegressionCV`) и долей меток в *y_train_150*, отличных от ID \n",
    " этого пользователя (именно такую долю правильных ответов можно получить, если классификатор всегда \"говорит\", что это не пользователь с номером $i$ в задаче классификации $i$-vs-All).**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class_distr = np.bincount(y_train_150.astype('int'))\n",
    "\n",
    "for user_id in np.unique(y_train_150):\n",
    "    ''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_better_than_default = (np.array(list(acc_diff_vs_constant.values())) > 0).sum()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**<font color='red'>Вопрос 7. </font> Посчитайте долю пользователей, для которых логистическая регрессия на кросс-валидации дает прогноз лучше константного. Округлите до 3 знаков после запятой.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Дальше будем строить кривые обучения для конкретного пользователя, допустим, для 128-го. Составьте новый бинарный вектор на основе *y_150users*, его значения будут 1 или 0 в зависимости от того, равен ли ID-шник пользователя 128.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_binary_128 = ''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.model_selection import learning_curve\n",
    "\n",
    "def plot_learning_curve(val_train, val_test, train_sizes, \n",
    "                        xlabel='Training Set Size', ylabel='score'):\n",
    "    def plot_with_err(x, data, **kwargs):\n",
    "        mu, std = data.mean(1), data.std(1)\n",
    "        lines = plt.plot(x, mu, '-', **kwargs)\n",
    "        plt.fill_between(x, mu - std, mu + std, edgecolor='none',\n",
    "                         facecolor=lines[0].get_color(), alpha=0.2)\n",
    "    plot_with_err(train_sizes, val_train, label='train')\n",
    "    plot_with_err(train_sizes, val_test, label='valid')\n",
    "    plt.xlabel(xlabel); plt.ylabel(ylabel)\n",
    "    plt.legend(loc='lower right');"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Посчитайте доли правильных ответов на кросс-валидации в задаче классификации \"user128-vs-All\" в зависимости от размера выборки. Не помешает посмотреть встроенную документацию для *learning_curve*.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "train_sizes = np.linspace(0.25, 1, 20)\n",
    "estimator = svm_grid_searcher2.best_estimator_\n",
    "n_train, val_train, val_test = learning_curve ''' ВАШ КОД ЗДЕСЬ '''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_learning_curve(val_train, val_test, n_train, \n",
    "                    xlabel='train_size', ylabel='accuracy')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Сделайте выводы о том, помогут ли алгоритму новые размеченные данные при той же постановке задачи.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Пути улучшения\n",
    "- конечно, можно проверить еще кучу алгоритмов, например, Xgboost, но в такой задаче очень маловероятно, что что-то справится лучше линейных методов\n",
    "- интересно проверить качество алгоритма на данных, где сессии выделялись не по количеству посещенных сайтов, а по времени, например, 5, 7, 10 и 15 минут. Отдельно стоит отметить данные нашего [соревнования](https://inclass.kaggle.com/c/catch-me-if-you-can-intruder-detection-through-webpage-session-tracking2) \n",
    "- опять же, если ресурсы позволяют, можно проверить, насколько хорошо можно решить задачу для 3000 пользователей\n",
    "\n",
    "\n",
    "На следующей неделе мы вспомним про линейные модели, обучаемые стохастическим градиентным спуском, и порадуемся тому, насколько быстрее они работают. Также сделаем первые (или не первые) посылки в [соревновании](https://inclass.kaggle.com/c/catch-me-if-you-can-intruder-detection-through-webpage-session-tracking2) Kaggle Inclass."
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
